﻿using System;
using System.Net;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Diagnostics;
using System.Windows.Media.Imaging;

namespace SilverlightPlayground.RFB.Readers
{
    public class RawRectReader : RfbClient.RfbRectReader
    {
        public const string ReceivingRects = "ReceivingRects";

        /// <summary>
        /// Initializes a new instance of the <see cref="RawRectReader"/> class.
        /// </summary>
        /// <param name="rfb">The RFB.</param>
        public RawRectReader(RfbClient rfb)
            : base(rfb)
        {
#if TRACE
            Debug.WriteLine("RawRectReader.ctor(rfb={0})", rfb.ServerName);
#endif
        }

        /// <summary>
        /// Gets or sets the current rect.
        /// </summary>
        /// <value>The current rect.</value>
        public Rect CurrentRect { get; set; }

        /// <summary>
        /// Reads the specified rect.
        /// </summary>
        /// <param name="rect">The rect.</param>
        public override void Read(Rect rect)
        {
#if TRACE
            Debug.WriteLine("RawRectReader.Read(rect={0})", rect);
#endif
            this.CurrentRect = rect;
            this.Receive((int)(rect.Width * rect.Height * this.RFB.BytesPerPlane), RawRectReader.ReceivingRects);
        }

        /// <summary>
        /// Called when [receive].
        /// </summary>
        /// <param name="data">The data.</param>
        public override void OnReceive(byte[] data, string action, object state)
        {
#if TRACE
            Debug.WriteLine("RawRectReader.OnReceive(data.Length={0})", data.Length);
#endif
            switch(action)
            {
                case RawRectReader.ReceivingRects:
                    this.EndReceiveRect(data);
                    break;
                default:
                    throw new Exception("Unknown status");
            }
        }

        private void EndReceiveRect(byte[] data)
        {
#if TRACE
            Debug.WriteLine("RawRectReader.EndReceiveRect(data.Length={0})", data.Length);
#endif

            try
            {
                this.OnRawUpdate(
                    this.CurrentRect,
                    this.Decode(this.CurrentRect, data));

                this.OnCompleted();
            }
            catch (OverflowException)
            {
                // Swallow this exception. For some strange reason UltraVNC sends X and Y > 65K.
            }
            catch (ArgumentException)
            {
                // Swallow this exception. For some strange reason UltraVNC sends invalid values.
            }
            catch (Exception ex)
            {
                this.OnFailure(ex);
            }
        }

        /// <summary>
        /// Decodes the specified rect.
        /// </summary>
        /// <param name="rect">The rect.</param>
        /// <param name="rawbytes">The rawbytes.</param>
        /// <returns></returns>
        private byte[] Decode(Rect rect, byte[] rawbytes)
        {
            byte[] bmap = new byte[(int)rect.Width * (int)rect.Height * 4];

            for (int i = 0; i < rawbytes.Length; i += this.RFB.BytesPerPlane)
            {
                UInt32 pixel = RfbProtoUtil.GetPixelFromData(rawbytes, i, this.RFB.BytesPerPlane);
                int disp = i / this.RFB.BytesPerPlane * 4;
                bmap[disp + 3] = 0xFF;
                bmap[disp + 2] = (byte)(((pixel >> this.RFB.RedShift) & this.RFB.RedMax) * 255 / this.RFB.RedMax);
                bmap[disp + 1] = (byte)(((pixel >> this.RFB.GreenShift) & this.RFB.GreenMax) * 255 / this.RFB.GreenMax);
                bmap[disp + 0] = (byte)(((pixel >> this.RFB.BlueShift) & this.RFB.BlueMax) * 255 / this.RFB.BlueMax);
            }

            return bmap;
        }
    }
}
